package net.hangar5.xmlrpc;

/* RpcServer.java

The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.

The Original Code is "Hangar5 XMLRPC Library".

The Initial Developer of the Original Code is James D. Rudnicki.
Portions created by James D. Rudnicki are
Copyright (C) 2001.  All Rights Reserved.

Contributor(s):
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.io.PrintWriter;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.util.*;
import java.net.ServerSocket;

import uk.co.wilson.net.http.*;

/** Base class for stand-alone HTTP server.
 * Objects derived from this class can act as HTTP servers for
 * XML-RPC calls.
 * @see net.hangar5.xmlrpc.test.H5RpcTest
 *
 * This class is derived from the stand-alone server in
 * the MinML XML-RPC package.
 */
public class RpcServer extends MinMLHTTPServer
{
  protected Dispatcher dispatcher;

  protected class RpcWorker extends HTTPWorker
  {

	public RpcWorker()
        {
	  return;
	}

	/**
	 * POST is how XMLRPC calls are rx'd
	 */
	protected void processPost(final InputStream in,
							   final OutputStream res,
							   final String uri,
							   final String version)
	  throws Exception
	{
	  StringBuffer sbResp;

	  /* read entire request into a StringBuffer
	  The idea here is that it is pointless to call read
	  at a rate much higher than the net IO speed of the TINI with
	  the ethernet adapter.  I'm trying to get fewer calls with larger
	  numbers of bytes, as opposed to many calls with a few bytes.
	  These numbers are rough guesses of about 50 kbyte / s.  */
	  final int nSizeChunk = 250;
	  final int nWaitBetweenChunks = 5;
	  final int nTimeout = 20000;
	  int nTotal;

	  Reader rdCall = new InputStreamReader(in);
	  StringBuffer sbCall = new StringBuffer(1024);
	  char[] cBuff = new char[nSizeChunk];
	  int n;

	  nTotal = 0;
	  /* Read stream
	   Two end conditions occur hear.
	   with keep alive, the stream remains open, but the count reaches the
	   limit int the HTTP header.  The Reader will return -1 when the limit
	   is reached.
	   Without keep alive, the stream closes once it is read fully.
	  */
	  do
          {
		if( rdCall.ready() )
                {
		  n = rdCall.read( cBuff, 0, nSizeChunk );
		}
		else
                {
		  n = -1;
		}

		if( n > 0 )
                {
		  sbCall.append( cBuff, 0, n );
		}

		try
                {
		  nTotal += nWaitBetweenChunks;
		  if( nTotal > nTimeout )
                  {
			throw new IOException();
		  }
		  Thread.sleep( nWaitBetweenChunks );
		}
		catch( InterruptedException x ) {}
	  }
          while( n>=0 );

	  sbResp = dispatcher.executePost( uri, sbCall );

	  long l1, l2;
	  res.write( version.getBytes() );
	  res.write( okMessage );
	  res.write( contentTypeXML );
	  res.write( keepConnection );
	  res.write( contentLength );
	  res.write( Integer.toString( sbResp.length() ).getBytes() );
	  res.write( endOfLine );
	  res.write( endOfLine );
	  res.write( sbResp.toString().getBytes() );
	  res.flush();
	  return;
	}

  } // end RpcWorker

  public static final byte[] contentTypeXML = "Content-Type: text/xml\r\n".getBytes();
  public static final byte[] contentLength = "Content-Length: ".getBytes();

  public RpcServer( final ServerSocket serverSocket,
					   final int minWorkers,
					   final int maxWorkers,
					   final int maxKeepAlives,
					   final int workerIdleLife,
					   final int socketReadTimeout)
  {
	super(serverSocket, minWorkers, maxWorkers, maxKeepAlives, workerIdleLife, socketReadTimeout);
	init();
  }

  public Dispatcher getDispatcher()
  {
	return dispatcher;
  }
  protected void init()
  {
	dispatcher = new Dispatcher();
  }
  protected Worker makeNewWorker() {
	return new RpcWorker();
  }
  protected void setDispatcher( Dispatcher d )
  {
	dispatcher = d;
  }
} // end RpcServer
